use crate::ctx::WasiCtx;
use wasmtime::component::ResourceTable;
pub use wasmtime_wasi_io::{IoImpl, IoView};

/// A trait which provides access to the [`WasiCtx`] inside the embedder's `T`
/// of [`Store<T>`][`Store`].
///
/// This crate's WASI Host implementations depend on the contents of
/// [`WasiCtx`]. The `T` type [`Store<T>`][`Store`] is defined in each
/// embedding of Wasmtime. These implementations are connected to the
/// [`Linker<T>`][`Linker`] by the
/// [`add_to_linker_sync`](crate::add_to_linker_sync) and
/// [`add_to_linker_async`](crate::add_to_linker_async) functions.
///
/// The [`WasiView`] trait implies the [`IoView`] trait, so each `T` must
/// also contain a [`ResourceTable`] and impl `IoView`.
///
/// # Example
///
/// ```
/// use wasmtime_wasi::{WasiCtx, ResourceTable, WasiView, IoView, WasiCtxBuilder};
///
/// struct MyState {
///     ctx: WasiCtx,
///     table: ResourceTable,
/// }
///
/// impl IoView for MyState {
///     fn table(&mut self) -> &mut ResourceTable { &mut self.table }
/// }
/// impl WasiView for MyState {
///     fn ctx(&mut self) -> &mut WasiCtx { &mut self.ctx }
/// }
/// ```
/// [`Store`]: wasmtime::Store
/// [`Linker`]: wasmtime::component::Linker
/// [`ResourceTable`]: wasmtime::component::ResourceTable
///
pub trait WasiView: IoView {
    /// Yields mutable access to the [`WasiCtx`] configuration used for this
    /// context.
    fn ctx(&mut self) -> &mut WasiCtx;
}

impl<T: ?Sized + WasiView> WasiView for &mut T {
    fn ctx(&mut self) -> &mut WasiCtx {
        T::ctx(self)
    }
}

impl<T: ?Sized + WasiView> WasiView for Box<T> {
    fn ctx(&mut self) -> &mut WasiCtx {
        T::ctx(self)
    }
}

/// A small newtype wrapper which serves as the basis for implementations of
/// `Host` WASI traits in this crate.
///
/// This type is used as the basis for the implementation of all `Host` traits
/// generated by `bindgen!` for WASI interfaces. This is used automatically with
/// [`add_to_linker_sync`](crate::add_to_linker_sync) and
/// [`add_to_linker_async`](crate::add_to_linker_async).
///
/// This type is otherwise provided if you're calling the `add_to_linker`
/// functions generated by `bindgen!` from the [`bindings`
/// module](crate::bindings). In this situation you'll want to create a value of
/// this type in the closures added to a `Linker`.
#[repr(transparent)]
pub struct WasiImpl<T>(pub IoImpl<T>);

impl<T: IoView> IoView for WasiImpl<T> {
    fn table(&mut self) -> &mut ResourceTable {
        T::table(&mut self.0 .0)
    }
}
impl<T: WasiView> WasiView for WasiImpl<T> {
    fn ctx(&mut self) -> &mut WasiCtx {
        T::ctx(&mut self.0 .0)
    }
}
